#include <vector>
#include <queue>
#include "sched_RSDL.h"
#include "basesched.h"
#include <iostream>
#include <string>

using namespace std;
 
SchedRSDL::SchedRSDL(vector<int> argn) {
        cantprioridades = argn[1]; //guardo la cantidad de prioridades.
        std::vector<int> vector2 ;
        for (int j = 2 ; j < cantprioridades + 2 ; j++){ //cargo los quantum individuales de cada prioridad. 
            vector2.push_back(argn[j]);

       }
        qprioridades = vector2;


        std::vector<std::queue<int> > cola1 (cantprioridades);//armo los scheduler "activo" y "expirado".
        std::vector<std::queue<int> > cola2 (cantprioridades);
    	activo = cola1;
        expirado = cola2;
        int min = qprioridades[0];
        for (int i = 0; i < cantprioridades ; i++){
            if (min > qprioridades[i]) {min = qprioridades[i];}
        }
        quantum = min/2;
        prioactual = 0;
        qcola = 0;
        
}
SchedRSDL::~SchedRSDL() {

}

void SchedRSDL::load(int pid) {

    prioridadportareas.push_back(cantprioridades/2); //guardo en prioridadportareas[pid] la prioridad de la tarea. La colocamos en la prioridad del medio para la siguiente Major Rotation.
    activo[prioactual].push(pid); //cargo la tarea en su nivel de prioridad correspondiente.

}

void SchedRSDL::unblock(int pid) {
	activo[prioactual].push(pid); //al desbloquearse la tarea, se la carga en el nivel actual de prioridad, que es el más alto disponible, para que siga siendo justo su tiempo de ejecución.
    if (prioridadportareas[pid] != 0){
        prioridadportareas[pid] =     prioridadportareas[pid] - 1;//aumento la prioridad de las tareas interactivas.
    }
}

int SchedRSDL::tick(int cpu, const enum Motivo m) {
		int res = 0;

		if (m == EXIT) {//Si termina una tarea...
	                quantumtarea = 0;
			if (!(activovacio())){//...y hay tareas activas:

				buscoSiguientePrio(); //busco el siguiente nivel de prioridad no-vacío.
				res = activo[prioactual].front(); //pido la primer tarea de ese nivel de prioridad.
				activo[prioactual].pop();//la quito del scheduler.
            		}
			else if(expiradovacio()){//...y no quedan tareas ni activas ni expiradas, terminé y devuelvo IDLE.
				res = IDLE_TASK;
			}
			else { //...y no hay tareas activas pero si hay tareas expiradas:
		                majorRotation();
				buscoSiguientePrio();//itero hasta encontrar un nivel de prioridad no-vacío.
				res = activo[prioactual].front();//pido la primer tarea de ese nivel de prioridad.
				activo[prioactual].pop();//la quito del scheduler.
			}
		}
		else if(m == BLOCK){//Si se bloquea una tarea...
			quantumtarea = 0;
			if (!(activovacio())){//...y hay tareas activas:
                		buscoSiguientePrio();//busco el siguiente nivel de prioridad no-vacío.

				res = activo[prioactual].front();//pido la primer tarea de ese nivel de prioridad.
                		activo[prioactual].pop(); //la quito del scheduler.
 

            }
			else if(expiradovacio()){//...y no quedan tareas activas ni expiradas, terminé y devuelvo IDLE.
				res = IDLE_TASK;
			}
			else if (activovacio() && !(expiradovacio())){//...y no hay tareas activas pero si expiradas:
     		        	majorRotation();
				buscoSiguientePrio();//itero hasta encontrar un nivel de prioridad no-vacío.
				res = activo[prioactual].front();//pido la primer tarea de ese nivel de prioridad.
				activo[prioactual].pop();//la quito del scheduler.
                
			}

		}
		else if(m == TICK) {
            		quantumtarea++;//aumento en quantum de la tarea actual.
			qcola++;//aumento el quantum de la cola actual de prioridad.
			if(qcola == qprioridades[prioactual]){//Si se acaba el quantum del nivel de prioridad.
                		quantumtarea = 0;//reseteo el contador de quantum para la tarea.
				qcola = 0;//reseteo el contador de quantum para el nivel actual de prioridad.
		        	if(!(current_pid(cpu) == IDLE_TASK)){
		           		activo[prioactual].push(current_pid(cpu));//dejo de ejecutar la tarea actual

					while(!(activo[prioactual].empty())){//mientras queden tareas en el nivel actual de prioridad...
		           			int pidactual = activo[prioactual].front();//pido el pid de la tarea que estoy moviendo.
						if(prioactual < (cantprioridades - 1)){//si no estoy en el último nivel de prioridad:
							activo[prioactual+1].push(pidactual);//la cargo en el nivel inferior al actual.
		           			}else{//si estoy en el último nivel:

	   						expirado[prioridadportareas[pidactual]].push(pidactual);//la cargo en el scheduler expirado, en el nivel de prioridad que le corresponde.
						}
						activo[prioactual].pop();//...y la remuevo de su posición actual.
					}
		       
					if(prioactual < (cantprioridades - 1)){//si no estoy en el último nivel de prioridad...
				    		prioactual++;//paso a la siguiente prioridad.
						res = activo[prioactual].front();//pido la primer tarea de ese nivel de prioridad.
						activo[prioactual].pop();//la quito del scheduler.
				 	}
			       		else{
						majorRotation();
						buscoSiguientePrio();//itero hasta encontrar un nivel de prioridad no-vacío.
						res = activo[prioactual].front();//pido la primer tarea de ese nivel de prioridad.
						activo[prioactual].pop();//la quito del scheduler.
		       			}
	       			}
            			else { res = IDLE_TASK;}
			}
			else{//si queda quantum en el nivel actual:
                	if (quantumtarea == quantum){//si se le termina el quantum a la tarea actual:
                    		quantumtarea = 0; //reseteo el quantum asignado a las tareas.
				if(prioactual < (cantprioridades - 1)){//si no estoy en el último nivel de prioridad:
                        		activo[prioactual+1].push(current_pid(cpu));//la cargo en el nivel inferior al actual.

					buscoSiguientePrio();//itero hasta encontrar un nivel de prioridad no-vacío.
   					res = activo[prioactual].front();//pido la primer tarea de ese nivel de prioridad.
					activo[prioactual].pop();//la quito del scheduler.
                    		}                    
                    		else {//si estoy en el último nivel:

                        		(expirado[prioridadportareas[current_pid(cpu)]]).push(current_pid(cpu)); //La cargo en su nivel de prioridad correspondiente en el scheduler expirado.

				        if(activovacio()){
				            majorRotation();
				        }
				        buscoSiguientePrio();//itero hasta encontrar un nivel de prioridad no-vacío.
				        res = activo[prioactual].front();//pido la primer tarea de ese nivel de prioridad.
				        activo[prioactual].pop();//la quito del scheduler.
                    		}

               		}
                	else {//si hay quantum restante
                    		if(current_pid(cpu) == IDLE_TASK){//y la tarea actual es IDLE:
                            		quantumtarea = 0; //reseteo el quantum de la tarea.
		                	if (!(activovacio())){//si hay tareas activas:
                                		buscoSiguientePrio();//busco el siguiente nivel de prioridad no-vacío.
				                res = activo[prioactual].front();//pido la primer tarea de ese nivel de prioridad.
                                	  	activo[prioactual].pop(); //la quito del scheduler.
		                 	}
			                else if(expiradovacio()){//si no quedan tareas ni activas ni expiradas, terminé y devuelvo IDLE.
				                res = IDLE_TASK;
			                }
			                else { //si no hay tareas activas pero si hay tareas expiradas:
			                    	majorRotation();
				                buscoSiguientePrio();//itero hasta encontrar un nivel de prioridad no-vacío.
				                res = activo[prioactual].front();//pido la primer tarea de ese nivel de prioridad.
				                activo[prioactual].pop();//la quito del scheduler.

				            }
                     		}
                   		else{//si hay quantum restante, y la tarea no es IDLE:
					    res = current_pid(cpu);//devuelvo la tarea actual hasta que se termine su quantum.
			      	}
               		}   
         	}
	}
	else{
	// m == ERROR;
	cerr << "Error"<< endl;
	return -2;
    	}

	return res;
}


void SchedRSDL::buscoSiguientePrio(){
	int viejo = prioactual;
	while(prioactual < cantprioridades){

		if(!(activo[prioactual].empty())){
			break;
		}
        prioactual++;
	}

	if(viejo != prioactual)
		qcola = 0;

}

bool SchedRSDL::activovacio(){//veo si está vacío un vector de colas.
    int i = 0;
    for (i = 0; i < cantprioridades; i++){//mientras haya colas de tareas:
        if (!(activo[i]).empty()) break;//si encuentro una no-vacía, paro.

    }

return !(i < cantprioridades); //Si i < cantprioridades, encontré un elemento, por lo tanto es falso que "activo" esté vacío.

}

bool SchedRSDL::expiradovacio(){//veo si está vacío un vector de colas.
    int i = 0;
    for (i = 0; i < cantprioridades; i++){//mientras haya colas de tareas:
        if (!(expirado[i]).empty()) break;//si encuentro una no-vacía, paro.

    }
return !(i < cantprioridades); //Si i < cantprioridades, encontré un elemento, por lo tanto es falso que "expirado" esté vacío.

}
void SchedRSDL::limpiarexpirado(){//borra muy a lo bestia el scheduler expirado y lo re-arma en blanco.
    for (int i = 0; i < cantprioridades; i++){//mientras haya colas de tareas:
        expirado.pop_back();//eliminar una de ellas.
    }
    std::vector<std::queue<int> > temp (cantprioridades);//creo un nuevo vector de [cantprioridades] colas.
    expirado = temp;//es el nuevo "expirado".

}

void SchedRSDL::majorRotation(){

    for(unsigned int i = 0; i < prioridadportareas.size(); i++){
        if(!(prioridadportareas[i] < cantprioridades - 1) )
            prioridadportareas[i] = prioridadportareas[i] + 1;
    }

    activo = expirado;//re-armo el scheduler activo con las expiradas restantes.
    limpiarexpirado();//limpio "expirado".
    qcola = 0;//pongo el quantum de la prioridad actual en 0.
    prioactual = 0;//comienzo por la prioridad 0.

}
